<?php

namespace app\common\library;

use app\admin\model\Admin;
use app\admin\model\AuthRule;
use fast\Random;
use think\Config;
use think\Db;
use think\Exception;
use think\Hook;
use think\Request;

class TAuth
{
    protected static $instance = null;
    protected $_error = '';
    protected $_logined = false;
    protected $_admin = null;
    protected $_token = '';
    //Token默认有效时长
    protected $keeptime = 2592000;
    protected $requestUri = '';
    protected $rules = [];
    //默认配置
    protected $_flag = 2;
    protected $config = [];
    protected $options = [];
    protected $allowFields = ['id', 'username', 'nickname', 'avatar', 'mobile', 'dept_id', 'position_ids', 'open_id', 'open_avatar', 'open_nickname', 'admitGuid'];

    public function __construct($options = [])
    {
        if ($config = Config::get('admin')) {
            $this->config = array_merge($this->config, $config);
        }
        $this->options = array_merge($this->config, $options);
    }

    /**
     *
     * @param array $options 参数
     * @return TAuth
     */
    public static function instance($options = [])
    {
        if (is_null(self::$instance)) {
            self::$instance = new static($options);
        }

        return self::$instance;
    }

    /**
     * 获取Admin模型
     * @return Admin
     */
    public function getAdmin()
    {
        return $this->_admin;
    }

    /**
     * 兼容调用Admin模型的属性
     *
     * @param string $name
     * @return mixed
     */
    public function __get($name)
    {
        return $this->_admin ? $this->_admin->$name : null;
    }

    /**
     * 兼容调用Admin模型的属性
     */
    public function __isset($name)
    {
        return isset($this->_admin) ? isset($this->_admin->$name) : false;
    }

    /**
     * 根据Token初始化
     *
     * @param string $token Token
     * @return boolean
     */
    public function init($token)
    {
        if ($this->_logined) {
            return true;
        }
        if ($this->_error) {
            return false;
        }
        $data = Token::get($token);
        if (!$data) {
            return false;
        }
        $admin_id = intval($data['user_id']);
        if ($admin_id > 0) {
            $admin = Admin::get($admin_id);
            if (!$admin) {
                $this->setError('Account not exist');
                return false;
            }
            if ($admin['status'] != 'normal') {
                $this->setError('Account is locked');
                return false;
            }
            $this->_admin = $admin;
            $this->_logined = true;
            $this->_token = $token;

            //初始化成功的事件
            Hook::listen("admin_init_successed", $this->_logined);

            return true;
        } else {
            $this->setError('You are not logged in');
            return false;
        }
    }

    /**
     * 用户登录
     *
     * @param string $account  账号,用户名、邮箱、手机号
     * @param string $password 密码
     * @return boolean
     */
    public function login($account, $password)
    {
        $admin = Admin::get(['mobile' => $account]);
        if (!$admin) {
            $this->setError('Account is incorrect');
            return false;
        }

        if ($admin->status != 'normal') {
            $this->setError('Account is locked');
            return false;
        }
        if ($admin->password != $this->getEncryptPassword($password, $admin->salt)) {
            $this->setError('Password is incorrect');
            return false;
        }

        //直接登录会员
        return $this->direct($admin->id);
    }

    /**
     * 退出
     *
     * @return boolean
     */
    public function logout()
    {
        if (!$this->_logined) {
            $this->setError('You are not logged in');
            return false;
        }
        //设置登录标识
        $this->_logined = false;
        //删除Token
        Token::delete($this->_token);
        //退出成功的事件
        Hook::listen("admin_logout_successed", $this->_admin);
        return true;
    }

    /**
     * 修改密码
     * @param string $newpassword       新密码
     * @param string $oldpassword       旧密码
     * @param bool   $ignoreoldpassword 忽略旧密码
     * @return boolean
     */
    public function changepwd($newpassword, $oldpassword = '', $ignoreoldpassword = false)
    {
        if (!$this->_logined) {
            $this->setError('You are not logged in');
            return false;
        }
        //判断旧密码是否正确
        if ($this->_admin->password == $this->getEncryptPassword($oldpassword, $this->_admin->salt) || $ignoreoldpassword) {
            Db::startTrans();
            try {
                $salt = Random::alnum();
                $newpassword = $this->getEncryptPassword($newpassword, $salt);
                $this->_admin->save(['loginfailure' => 0, 'password' => $newpassword, 'salt' => $salt]);

                Token::delete($this->_token);
                //修改密码成功的事件
                Hook::listen("admin_changepwd_successed", $this->_admin);
                Db::commit();
            } catch (Exception $e) {
                Db::rollback();
                $this->setError($e->getMessage());
                return false;
            }
            return true;
        } else {
            $this->setError('Password is incorrect');
            return false;
        }
    }

    /**
     * 直接登录账号
     * @param int $admin_id
     * @return boolean
     */
    public function direct($admin_id)
    {
        $admin = Admin::get($admin_id);
        if ($admin) {
            Db::startTrans();
            try {
                $ip = request()->ip();
                $time = time();

                //记录本次登录的IP和时间
                $admin->loginip = $ip;
                $admin->logintime = $time;

                $admin->save();

                $this->_admin = $admin;

                Token::clear($this->_flag, $admin->id);

                $this->_token = Random::uuid();
                Token::set($this->_token, $this->_flag, $admin->id, $this->keeptime);

                $this->_logined = true;

                //登录成功的事件
                Hook::listen("admin_login_successed", $this->_admin);
                Db::commit();
            } catch (Exception $e) {
                Db::rollback();
                $this->setError($e->getMessage());
                return false;
            }
            return true;
        } else {
            return false;
        }
    }

    /**
     * 检测是否是否有对应权限
     * @param string $path   控制器/方法
     * @param string $module 模块 默认为当前模块
     * @return boolean
     */
    public function check($path = null, $module = null)
    {
        if (!$this->_logined) {
            return false;
        }

        $ruleList = $this->getRuleList();
        $rules = [];
        foreach ($ruleList as $k => $v) {
            $rules[] = $v['name'];
        }
  
        return in_array($path, $rules);
    }

    /**
     * 判断是否登录
     * @return boolean
     */
    public function isLogin()
    {
        if ($this->_logined) {
            return true;
        }
        return false;
    }

    /**
     * 获取当前Token
     * @return string
     */
    public function getToken()
    {
        return $this->_token;
    }

    /**
     * 获取会员基本信息
     */
    public function getAdmininfo()
    {
        $data = $this->_admin->toArray();
        $allowFields = $this->getAllowFields();
        $admininfo = array_intersect_key($data, array_flip($allowFields));
        $admininfo = array_merge($admininfo, Token::get($this->_token));

        return $admininfo;
    }

    /**
     * 获取会员组别规则列表
     * @return array|bool|\PDOStatement|string|\think\Collection
     */
    public function getRuleList()
    {
        $group = Db::name('auth_group_access')->alias('aga')
        ->field('ag.rules')
        ->join('auth_group ag', 'aga.group_id = ag.id', 'LEFT')
        ->where("aga.uid='{$this->_admin->id}' and ag.status='normal'")
        ->find();
        
        if($group && isset($group['rules'])) {
            if(strpos($group['rules'], '*') !== false) {
                $this->rules = AuthRule::where(['type' => 'api', 'status' => 'normal'])->field('id,pid,name,title,url')->select();
            }else {
                $rules = explode(',', $group['rules']);
                $this->rules = AuthRule::where(['type' => 'api', 'status' => 'normal'])->where('id', 'in', $rules)->field('id,pid,name,title,url')->select();
            }
        }

        return $this->rules;
    }

    /**
     * 根据用户id获取用户组,返回值为数组
     * @param int $uid  用户id
     * @return array       用户所属的用户组 array(
     *                  array('uid'=>'用户id','group_id'=>'用户组id','name'=>'用户组名称','rules'=>'用户组拥有的规则id,多个,号隔开'),
     *                  ...)
     */
    public function getGroups($uid)
    {
        static $groups = [];
        if (isset($groups[$uid])) {
            return $groups[$uid];
        }
        // 执行查询
        $user_groups = Db::name('auth_group_access')
            ->alias('aga')
            ->join('__' . strtoupper('auth_group') . '__ ag', 'aga.group_id = ag.id', 'LEFT')
            ->field('aga.uid,aga.group_id,ag.id,ag.pid,ag.name,ag.rules')
            ->where("aga.uid='{$uid}' and ag.status='normal'")
            ->select();
        
        $groups[$uid] = $user_groups ?: [];
        return $groups[$uid];
    }


    public function isAdmin()
    {
        $groups = $this->getGroups($this->_admin->id);
        foreach ($groups as $key => $value) {
            if ($value['group_id'] == 1 || $value['group_id'] == 2) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取当前请求的URI
     * @return string
     */
    public function getRequestUri()
    {
        return $this->requestUri;
    }

    /**
     * 设置当前请求的URI
     * @param string $uri
     */
    public function setRequestUri($uri)
    {
        $this->requestUri = $uri;
    }

    /**
     * 获取允许输出的字段
     * @return array
     */
    public function getAllowFields()
    {
        return $this->allowFields;
    }

    /**
     * 设置允许输出的字段
     * @param array $fields
     */
    public function setAllowFields($fields)
    {
        $this->allowFields = $fields;
    }

    /**
     * 获取密码加密后的字符串
     * @param string $password 密码
     * @param string $salt     密码盐
     * @return string
     */
    public function getEncryptPassword($password, $salt = '')
    {
        return md5(md5($password) . $salt);
    }

    /**
     * 检测当前控制器和方法是否匹配传递的数组
     *
     * @param array $arr 需要验证权限的数组
     * @return boolean
     */
    public function match($arr = [])
    {
        $request = Request::instance();
        $arr = is_array($arr) ? $arr : explode(',', $arr);
        if (!$arr) {
            return false;
        }
        $arr = array_map('strtolower', $arr);
        // 是否存在
        if (in_array(strtolower($request->action()), $arr) || in_array('*', $arr)) {
            return true;
        }

        // 没找到匹配
        return false;
    }

    /**
     * 设置会话有效时间
     * @param int $keeptime 默认为永久
     */
    public function keeptime($keeptime = 0)
    {
        $this->keeptime = $keeptime;
    }

    /**
     * 设置错误信息
     *
     * @param string $error 错误信息
     * @return Auth
     */
    public function setError($error)
    {
        $this->_error = $error;
        return $this;
    }

    /**
     * 获取错误信息
     * @return string
     */
    public function getError()
    {
        return $this->_error ? __($this->_error) : '';
    }

    /**
     * 获取部门信息
     */
    public function getDeptInfo($deptId = 0)
    {
        $deptId = $deptId ? $deptId : $this->_admin->dept_id;
        $info = Db::name('dts_dept')->where('id', $deptId)->find();
        
        return $info ? $info : [];
    }

    /**
     * 获取班级信息
     */
    public function getClassInfo($adminId = 0)
    {
        $adminId = $adminId ? $adminId : $this->_admin->id;

        $classes = Db::name('dts_classes')->alias('dc')
        ->field('dc.id,dc.name as classname,dc.grade_id,grade.name as gradename')
        ->join('dts_grade grade', 'grade.id = dc.grade_id')
        ->where('dc.monitor_id', $adminId)
        ->whereOrRaw('FIND_IN_SET('.$adminId.', dc.teacher_ids) > 0')
        ->find();

        return $classes ? $classes : ['id' => 0];
    }
}
